# patch.py
#
# Class to represent a patch in a radiosity scene

import xmlscene
from xmlscene_functions import *
from triangulatedpolygon import TriangulatedPolygon

# A patch is a triangle with radiosity properties
class Patch():
    def __init__(self, triangle=None, parent_polygon=None):
        self.triangle = triangle
        self.parent_polygon = parent_polygon
        self.radiosity = self.triangle.get_radiosity()

    def set_radiosity(self, radiosity):
        self.triangle.set_radiosity(radiosity)
        self.radiosity = self.triangle.get_radiosity()

    def get_radiosity(self):
        return self.radiosity

    def get_reflectivity(self):
        return self.triangle.get_reflectivity()

    def get_emission(self):
        return self.triangle.get_emission()

def createPatches(xml_scene, transformations, patch_area=1.0):
    # gather all the scene's objects
    scene_objects = []
    for object in xml_scene.get_body().get_object():
        scene_objects.append([object, identityTransformationMatrix()])
    for pairs in transformations:
        scene_objects.append(pairs)

    # for each object, triangulate it, subdivide the triangles, 
    # and create a patch for each triangle
    patches = []
    for pairs in scene_objects:
        object = pairs[0]
        transformation = pairs[1]
        objectdef = objectdefForObject(xml_scene, object)
        if objectdef is not None:
            objectdef_objects = objectDefObjects(objectdef)
            for objectdef_object in objectdef_objects:
                polygon = TriangulatedPolygon(object=objectdef_object, transformation_matrix=transformation, name=objectdef.get_name())
                polygon.divideTriangles(patch_area)
                for triangle in polygon.triangles:
                    patch = Patch(triangle = triangle,
                                  parent_polygon = polygon)
                    patches.append(patch)

    return patches

def computeRadiosityForPatch(patch, patches, patch_indices_to_form_factors, formfactor_threshold=0.0, highest_color_string=whiteRGBString()):
    if areRGBStringsEqual(patch.get_radiosity(), highest_color_string):
        # radiosity value already saturated, so do not compute anything
        return patch.get_radiosity()

    num_patches = len(patches)

    # for each form factor, multiply it by its patch's color to arrive
    # at the contribution from every other patch to this patch's color
    color = blackRGBString()
    for patch_index in patch_indices_to_form_factors:
        if patch_index >= num_patches:
            continue
        
        patch_color = patches[patch_index].get_radiosity()
        formfactor = patch_indices_to_form_factors[patch_index]
        if formfactor < formfactor_threshold:
            continue
        color = addRGBStrings(color, multiplyRGBStringBy(patch_color, formfactor))

    # factor in this patch's reflectivity and emission
    color = multiplyRGBStrings(color, patch.get_reflectivity())
    color = addRGBStrings(color, patch.get_emission())

    if isRGBStringGreaterThan(color, highest_color_string):
        color = highest_color_string

    return color

def convertPatchesToXmlScene(patches):
    # Each unique polygon, containing a set of patches, becomes a triangleset,
    # and each patch becomes a triangle
    xml_scene = xmlscene.scene.factory()
    head = xmlscene.head.factory()
    objectdef = xmlscene.objectdef.factory()
    objectdef.set_name("scene")
    polygons = []
    for patch in patches:
        if patch.parent_polygon is not None:
            if patch.parent_polygon not in polygons:
                polygons.append(patch.parent_polygon)
    trianglesets = []
    for polygon in polygons:
        triangleset = xmlscene.triangleset.factory()
        triangleset.set_name(polygon.name)
        triangles = []
        for triangle in polygon.triangles:
            triangles.append(triangle)
        triangleset.set_triangle(triangles)
        trianglesets.append(triangleset)
    objectdef.set_triangleset(trianglesets)
    objectdefs = [objectdef]
    head.set_objectdef(objectdefs)
    body = xmlscene.body.factory()
    object = xmlscene.object.factory()
    object.set_name("scene")
    objects = [object]
    body.set_object(objects)

    xml_scene.set_head(head)
    xml_scene.set_body(body)
    return xml_scene

if __name__ == '__main__':
    from xmlscenereader import readScene
    from xmlscenewriter import writeScene

    class TestPatches(unittest.TestCase):

        def setUp(self):
            self.infile = "./xml_test_files/test_scene_patch.xml"
            self.inscene = readScene(self.infile)
            self.patches = createPatches(self.inscene, applyMacros(self.inscene), 1000000.0) # to ensure no subdivision
            self.outfile = "./xml_test_files/test_scene_patch_rewritten.xml"

        def test_create_patches(self):
            self.assert_(len(self.patches) == 36) # 2 per xyplane, 2 per face of a cuboid, 6 faces, 2 cuboids => 2*6 + 2*6*2 = 36
            # TODO: rest

        def test_convert_patches_to_xml_scene(self):
            writeScene(convertPatchesToXmlScene(self.patches), self.outfile)
            self.patch_scene = readScene(self.outfile)
            # TODO: rest

    unittest.main()
